---
id: consent
title: Consent Flow
---

import useBaseUrl from '@docusaurus/useBaseUrl'
import Mermaid from '@theme/Mermaid'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'

OAuth2 and OpenID Connect require an authenticated End-User session for all
OAuth2 / OpenID Connect flows except the `client_credentials` flow which does
not involve End-Users.

ORY Hydra does not contain a database with End-Users but instead uses HTTP
Redirection to "delegate" the login and consent flow to another app - we call
this the Login & Consent App.

The following short video shows the flow from an End-User's perspective - it
includes both login and consent.

<iframe
  width="560"
  height="315"
  src="https://www.youtube.com/embed/txUmfORzu8Y"
  frameborder="0"
  allowfullscreen
></iframe>

:::info

Please read [Login Flow](./login) first, as the Login Flow happens before the
consent flow.

:::

The following sequence diagram describes the different API calls and HTTP
Redirects when performing the OAuth2 flow:

<Mermaid
  chart={`sequenceDiagram
    OAuth2 Client->>ORY Hydra: Initiates OAuth2 Authorize Code or Implicit Flow
    ORY Hydra-->>ORY Hydra: No end user session available (not authenticated)
    ORY Hydra->>Login Endpoint: Redirects end user with login challenge
    Login Endpoint-->ORY Hydra: Fetches login info
    Login Endpoint-->>Login Endpoint: Authenticates user with credentials
    Login Endpoint-->ORY Hydra: Transmits login info and receives redirect url with login verifier
    Login Endpoint->>ORY Hydra: Redirects end user to redirect url with login verifier
    ORY Hydra-->>ORY Hydra: First time that client asks user for permissions
    ORY Hydra->>Consent Endpoint: Redirects end user with consent challenge
    Consent Endpoint-->ORY Hydra: Fetches consent info (which user, what app, what scopes)
    Consent Endpoint-->>Consent Endpoint: Asks for end user's permission to grant application access
    Consent Endpoint-->ORY Hydra: Transmits consent result and receives redirect url with consent verifier
    Consent Endpoint->>ORY Hydra: Redirects to redirect url with consent verifier
    ORY Hydra-->>ORY Hydra: Verifies grant
    ORY Hydra->>OAuth2 Client: Transmits authorization code/token`}
/>

## Redirection to the Consent Endpoint

Once the login challenge is accepted, ORY Hydra will ask the user for consent:

<img
  src={useBaseUrl('/img/docs/google-consent-playground.png')}
  alt="Exemplary Consent Screen"
/>

The UI is fully under your control, because ORY Hydra redirects the End-User's
browser to the c

```yaml title="hydra serve all -c path/to/hydra/config.yml"
# Can also be set using the environment variable:
#   URLS_CONSENT=https://consent-app/consent
urls:
  consent: https://consent-app/consent
```

:::note

You can implement the Login and Consent endpoints in the same code base /
application. Check out our
[Login, Consent & Logout NodeJS Reference implementation](https://github.com/ory/hydra-login-consent-node)!

:::

ORY Hydra appends a `consent_challenge` query parameter to the url. The value is
a ID which should later be used by the Consent Endpoint to fetch important
information about the request.

```
https://consent-app/consent?consent_challenge=7bb518c4eec2454dbb289f5fdb4c0ee2
```

### Previous Consent

A consent has four distinctive attributes:

- The requested scope
  (`/oauth2/auth?...&...&scope=email+profile+offline_access`);
- The OAuth2 Client (`/oauth2/auth?client_id=abcd`);
- The End-User (this is the `subject` set in the [Login Flow](./login));
- Whether or not a previous consent exists, the previous consent has
  `remember: true`, and the scope granted by the user (did the user accept all
  of `email`, `profile`, `offline_access`?)

There are three possible states:

- The user has never before authorized ("consent") the OAuth2 Client before.
- The user has authorized ("consent") the OAuth2 Client before and chose to
  remember the "consent".
- The user has authorized ("consent") the OAuth2 Client before, and chose to
  remember the "consent", but the OAuth2 Client now also wants additional
  permissions ("has changed the token scope" in `/oauth2/auth?scope=...`).

Regardless of which of these three states we are in, the End-User's browser is
always redirected to the consent endpoint. What changes is the `skip` value, as
explained a bit later.

:::note

In certain scenarios (e.g. a special OAuth2 Client) you might not want to show
the consent screen at all. In those cases you can choose to skip showing the UI
and just accept the consent. Please keep in mind that OAuth2 is a delegation
protocol and that it makes most sense for third-party access. Not showing the
consent screen will break OpenID Connect Certification.

:::

## The Consent Endpoint

The Consent Endpoint (set by `urls.consent`) is an application written by you.
You can find an exemplary
[NodeJS reference implementation on our GitHub](https://github.com/ory/hydra-login-consent-node).

The Consent Endpoint uses the `consent_challenge` value in the URL to fetch
information about the consent request by making a HTTP GET request to:

```
http(s)://<HYDRA_ADMIN_URL>/oauth2/auth/requests/consent?consent_challenge=<challenge>
```

The response (see below in "Consent Challenge Response" tab) contains
information about the consent request. The body contains a `skip` value. If the
value is `false`, the user interface must be shown. If `skip` is true, you
should not show the user interface but instead just accept or reject the consent
request! For more details about the implementation check the
["Implementing the Consent Endpoint" Guide](../guides/login).

<Tabs
  defaultValue="ui"
  values={[
    {label: 'UI', value: 'ui'},
    {label: 'curl', value: 'curl'},
    {label: 'Consent Challenge Response', value: 'json'},
  ]}>
<TabItem value="ui">
<img src={useBaseUrl('/img/docs/consent-endpoint.png')}/>
</TabItem>
<TabItem value="curl">

```shell script
$ curl \
  "http://127.0.0.1:4445/oauth2/auth/requests/consent?consent_challenge=7bb518c4eec2454dbb289f5fdb4c0ee2"
```

Check the ["Implementing the Consent Endpoint" Guide](../guides/consent) for
examples using the ORY Hydra SDK in different languages.

</TabItem>
<TabItem value="json">

```json
{
  "challenge": "f633e49d56bc40e0a876ac8242eb9891",
  "requested_scope": ["openid", "offline"],
  "requested_access_token_audience": [],
  "skip": false,
  "subject": "foo@bar.com",
  "oidc_context": {
    "acr_values": [],
    "display": "",
    "id_token_hint_claims": {},
    "login_hint": "",
    "ui_locales": []
  },
  "client": {
    "client_id": "auth-code-client",
    "client_name": "",
    "redirect_uris": ["http://127.0.0.1:5555/callback"],
    "grant_types": ["authorization_code", "refresh_token"],
    "response_types": ["code", "id_token"],
    "scope": "openid offline",
    "audience": null,
    "owner": "",
    "policy_uri": "",
    "allowed_cors_origins": null,
    "tos_uri": "",
    "client_uri": "",
    "logo_uri": "",
    "contacts": null,
    "client_secret_expires_at": 0,
    "subject_type": "public",
    "token_endpoint_auth_method": "client_secret_basic",
    "userinfo_signed_response_alg": "none",
    "created_at": "2020-07-09T10:07:01Z",
    "updated_at": "2020-07-09T10:07:01Z"
  },
  "request_url": "http://127.0.0.1:4444/oauth2/auth?audience=&client_id=auth-code-client&max_age=0&nonce=shfxjszihgvbptswjbqsrdbg&prompt=&redirect_uri=http%3A%2F%2F127.0.0.1%3A5555%2Fcallback&response_type=code&scope=openid+offline&state=pmkekezifwwpgmzpckiqxzbt",
  "login_challenge": "de814daf9bbb4b788b505b3c2dd5ce20",
  "login_session_id": "c829da46-2041-400c-b72d-08324f878d0a",
  "acr": ""
}
```

</TabItem>
</Tabs>

The way you collect the consent information from the End-User is up to you. In
most cases, you will show an HTML form similar to:

```html
<form action="/consent" method="post">
  <input type="hidden" name="csrf_token" value="...." />
  <!-- Use CSRF tokens in your HTML forms! -->
  <input type="checkbox" name="scope" />
</form>
```

Once the End-User authenticated successfully, you either **accept** the login
challenge, or you **reject** (e.g. the user is not allowed to perform OAuth2
flows) the login challenge.

### Accepting the Consent Flow

To accept the Consent Challenge, make a HTTP PUT request with
`Content-Type: application/json` and a JSON payload (see
[Accept Consent Request HTTP API Reference](../reference/api#schemaacceptconsentrequest))

```json
{
  // The scope granted by the user.
  "grant_scope": ["openid", "offline_access"],

  // The following fields are optional

  // The Access Token Audience if needed. Typically equals the `requested_access_token_audience` field from
  // the consent challenge.
  "grant_access_token_audience": ["https://my-audience.com"],

  // Remember, if set to true, tells ORY Hydra to remember this consent authorization and reuse it if the same client
  // asks the same user for the same, or a subset of, scope.
  "remember": true,
  // RememberFor sets how long the consent authorization should be remembered for in seconds. If set to 0, the
  // authorization will be remembered indefinitely.
  "remember_for": 3600,

  // Set the data for this consent "session"
  "session": {
    "access_token": {
      "foo": "This field will be available when introspecting the Access Token"
    },
    "id_token": {
      "bar": "This field will be available as a claim in the ID Token"
    }
  }
}
```

With `curl` this might look like the following request:

```shell script
$ curl --location --request PUT 'http://127.0.0.1:4445/oauth2/auth/requests/consent/accept?consent_challenge=7bb518c4eec2454dbb289f5fdb4c0ee2' \
--header 'Content-Type: application/json' \
--data-raw '{
  "grant_scope": ["openid", "offline_access"]
}'
```

The server responds with JSON

```json
{
  "redirect_to": "http://127.0.0.1:4445/oauth2/auth..."
}
```

which is the URL your application must the End-User's browser to.

Check the ["Implementing the Login Endpoint" Guide](../guides/login) for
examples using the ORY Hydra SDK in different languages.

### Rejecting the Consent Flow

To reject the Login Challenge, make a HTTP PUT request with
`Content-Type: application/json` and a JSON payload (see
[Reject Consent Request HTTP API Reference](../reference/api#schemarejectrequest))

```json
{
  // The error should follow the OAuth2 error format (e.g. `invalid_request`, `login_required`).
  "error": "user_banned",

  // Description of the error in a human readable format.
  "error_description": "You are banned!",

  // Hint to help resolve the error.
  "error_hint": "Contact the site administrator.",

  // Debug contains information to help resolve the problem as a developer. Usually not exposed
  // to the public but only in the server logs.
  "error_debug": "The user was marked banned in the database.",

  // Represents the HTTP status code of the error (e.g. 401 or 403)
  //
  // Defaults to 400
  "status_code": 403
}
```

With `curl` this might look like the following request:

```shell script
$ curl --location --request PUT 'http://127.0.0.1:4445/oauth2/auth/requests/consent/reject?consent_challenge=7bb518c4eec2454dbb289f5fdb4c0ee2' \
--header 'Content-Type: application/json' \
--data-raw '{
  "error": "user_banned",
  "error_debug": "The user was marked banned in the database.",
  "error_description": "You are banned!",
  "error_hint": "Contact the site administrator.",
  "status_code": 403
}'
```

The server responds with JSON

```json
{
  "redirect_to": "http://127.0.0.1:4445/oauth2/auth..."
}
```

which is the URL your application must the End-User's browser to.

Check the ["Implementing the Login Endpoint" Guide](../guides/login) for
examples using the ORY Hydra SDK in different languages.

## Revoking Consent

You can revoke a user's consent either on a per application basis or for all
applications.

Revoking the consent will automatically revoke all related access and refresh
tokens.

:::warning

Do not use this endpoint to "invalidate" user sessions. Please revise your
approach to and usage of OAuth2 if you use access / refresh tokens as user
sessions (i.e. instead of browser cookies).

:::

Revoking all consent sessions of a user is as easy as sending `DELETE` to
`/oauth2/auth/sessions/consent?subject={subject}`.

Revoking the consent sessions of a user for a specific client is as easy as
sending `DELETE` to
`/oauth2/auth/sessions/consent?subject={subject}&client={client}`.
